package com.example.wdt.security;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.util.StreamUtils;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;


@Component
public class SignVerificationFilter extends OncePerRequestFilter {
    private static final String SIGN_PARAM = "sign";
    private static final String SECRET_KEY = "399d7e0cc3e4db4d86ab64045d1b50d1:46822471938776245fbdfbc14a4d4b09";
    private static final String secrte = "399d7e0cc3e4db4d86ab64045d1b50d1";

    /**
     * 计算签名
     * @param args 请求参数
     * @param secret secret
     * @return 签名
     */
    private String generateSign(Map<String, String> args, String secret)
    {
        StringBuilder sb = new StringBuilder();
        sb.append(secret);
        Map<String, String> sortedMap = new TreeMap<>(args);
        for (Map.Entry<String, String> item : sortedMap.entrySet())
        {
            if (item.getKey().equals("sign"))
                continue;

            sb.append(item.getKey());
            sb.append(item.getValue());
        }
        sb.append(secret);

        //System.out.println("sb:"+sb.toString());

        return md5(sb.toString());
    }

    private static String md5(String info)
    {
        try
        {
            // 获取 MessageDigest 对象，参数为 MD5 字符串，表示这是一个 MD5 算法（其他还有 SHA1 算法等）：
            MessageDigest md5 = MessageDigest.getInstance("MD5");
            // update(byte[])方法，输入原数据
            // 类似StringBuilder对象的append()方法，追加模式，属于一个累计更改的过程
            md5.update(info.getBytes(StandardCharsets.UTF_8));
            // digest()被调用后,MessageDigest对象就被重置，即不能连续再次调用该方法计算原数据的MD5值。可以手动调用reset()方法重置输入源。
            // digest()返回值16位长度的哈希值，由byte[]承接
            byte[] md5Array = md5.digest();
            // byte[]通常我们会转化为十六进制的32位长度的字符串来使用,本文会介绍三种常用的转换方法
            return bytesToHex(md5Array);
        }
        catch (NoSuchAlgorithmException e)
        {
            throw new RuntimeException(e);
        }
    }

    private static String bytesToHex(byte[] md5Array)
    {
        StringBuilder strBuilder = new StringBuilder();
        for (int i = 0; i < md5Array.length; i++)
        {
            int temp = 0xff & md5Array[i];
            String hexString = Integer.toHexString(temp);
            if (hexString.length() == 1)
            {// 如果是十六进制的0f，默认只显示f，此时要补上0
                strBuilder.append("0").append(hexString);
            }
            else
            {
                strBuilder.append(hexString);
            }
        }

        return strBuilder.toString();
    }

    @Override
    public void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
            throws ServletException, IOException {

        String sign = request.getParameter(SIGN_PARAM);
        if (sign == null) {
            sendJsonErrorResponse(response,"Missing sign parameter");
            response.sendError(0,"Missing sign parameter");
            return;
            //throw new BadCredentialsException("Missing sign parameter");
        }

        // 提取URL参数（除了sign之外）
        Map<String, String> params = new HashMap<>();
        request.getParameterMap().forEach((key, values) -> {
            if (!SIGN_PARAM.equals(key)) {
                params.put(key, values[0]);
            }
        });

        // 包装请求，使其可重复读取
        CachedBodyHttpServletRequestWrapper wrappedRequest = new CachedBodyHttpServletRequestWrapper (request);

        // 获取并读取请求体
        String requestBody = StreamUtils.copyToString(wrappedRequest.getInputStream(), StandardCharsets.UTF_8);

        // 生成预期的签名
        params.put("body", requestBody);
        String expectedSign = generateSign(params,this.secrte);

        // 验证签名
        if (!expectedSign.equals(sign)) {
            //System.out.println("sign:"+sign);
            //System.out.println("expectedSign:"+expectedSign);
            sendJsonErrorResponse(response,"Invalid sign");
            response.sendError(0,"Invalid sign");
            return;
            //throw new BadCredentialsException("Invalid sign");
        }

        chain.doFilter(wrappedRequest, response);
    }

    private void sendJsonErrorResponse(HttpServletResponse response, String errorMessage) throws IOException {
        response.setStatus(HttpServletResponse.SC_BAD_REQUEST); // 设置HTTP状态码为400 Bad Request
        response.setContentType(MediaType.APPLICATION_JSON_VALUE); // 设置响应内容类型为JSON

        // 创建错误响应的JSON对象
        Map<String, String> errorResponse = new HashMap<>();
        errorResponse.put("message", errorMessage);

        // 使用Jackson ObjectMapper将Map转换为JSON字符串
        ObjectMapper objectMapper = new ObjectMapper();
        String jsonResponse = objectMapper.writeValueAsString(errorResponse);

        // 将JSON字符串写入响应输出流
        response.getWriter().write(jsonResponse);
        response.getWriter().flush();
    }


}
